package ldh.im.fxbase.component;

import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.*;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.robot.Robot;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.text.Font;
import javafx.stage.Modality;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import ldh.im.fxbase.controller.CaptureControlController;
import ldh.im.fxbase.util.RegionUtil;
import ldh.im.fxbase.util.StageUtil;
import ldh.im.fxbase.util.ThreadPoolUtil;
import ldh.im.fxbase.vo.CanvasItem;
import ldh.im.fxbase.vo.CanvasPoint;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * 代码重构： 将背景图从ImageView改成Canvas
 */
public class ScreenCapturePane extends AnchorPane {

//    private Popup popup = new Popup();
    private Stage captureStage = null;
    private Rectangle rectangle = new Rectangle();
    private Rectangle maskRectangle = new Rectangle();
    private Label positionLabel = new Label();
    private Label tip = new Label("开始截屏");
    private RegionUtil.FxmlBean<HBox, CaptureControlController> fxmlBean;
    private StackPane controlPane = new StackPane();
    private Canvas canvas = new Canvas();
    private GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
    private Point2D origin = null;
    private Screen screen = null;
    private Robot robot = new Robot();
    private ImageView fullScreenImageView = new ImageView();
    private Canvas fullScreenCanvas = new Canvas();
    private GraphicsContext fullScreenGc = canvas.getGraphicsContext2D();

    private boolean canCapture = true;
    private List<CanvasItem> canvasItems = new ArrayList<>();
    private Image captureImage;
    private CanvasItem currentCanvasItem;
    private Point2D currentCanvasPoint = null;
    private Point2D originCanvasPoint = null;
    private TextField textField = new TextField();
    private boolean canvasItemChangePosition = false;  // cavas上的元素调整位置
    private Consumer<String> captureConsumer;
    private String imageCacheDir = System.getProperty("user.dir") + "/data/capture";//"F:\\tmp\\capture";

    public ScreenCapturePane() {
//        this.webView = webView;
        screen = Screen.getPrimary(); //getScreen(webView);
        fullScreenCanvas.setWidth(screen.getBounds().getWidth());
        fullScreenCanvas.setHeight(screen.getBounds().getHeight());
//        this.setPrefSize(screen.getBounds().getWidth(), screen.getBounds().getHeight());
//        this.setStyle("-fx-background-color: derive(lightgrey, 30%)");
        this.setStyle("-fx-background-color: rgb(0,0,0,0.01)");

        rectangle.setFill(Color.TRANSPARENT);
        rectangle.setStroke(Color.GREEN);

        controlPane.setStyle("-fx-background-color: derive(lightgrey, 30%)");

        maskRectangle.setWidth(screen.getBounds().getWidth());
        maskRectangle.setHeight(screen.getBounds().getHeight());
        maskRectangle.setStyle("-fx-background-color: derive(lightgrey, 20%)");
        maskRectangle.setOpacity(0.2);

        tip.setStyle("-fx-text-fill: yellow; -fx-font-size: 30px; ");
        tip.setLayoutX((screen.getBounds().getWidth()-100)/2);
        tip.setLayoutY(100);

        textField.setOnAction(e->{
            if (textField.isVisible() && currentCanvasItem != null && currentCanvasItem.getType() == 3) {
                textField.setVisible(false);
                currentCanvasItem.setText(textField.getText());
                draw();
                canvasItemChangePosition = true;
            };
        });
//        this.getChildren().addAll(fullScreenImageView, tip, rectangle, maskRectangle, positionLabel, canvas, controlPane, textField); // , position, canvas, controlPane
        this.getChildren().addAll(fullScreenCanvas, tip, rectangle, maskRectangle, positionLabel, canvas, controlPane, textField); // , position, canvas, controlPane

        initFullScreenCanvas();
        initPopup();

        initBind();
        initEvent();
    }

    public void stopCapture() {
        this.canCapture = false;
    }

    public void setCanvasType(int type) {
        currentCanvasItem = new CanvasItem(0, 0, 0, 0, Color.RED, type);
        if (type == 4) currentCanvasItem.setColor(Color.LIGHTGRAY);
        canvasItemChangePosition = false;
    }

    public void undoCanvas() {
        if(canvasItems.size() < 1) return;
        canvasItems.remove(canvasItems.size()-1);
        draw();
    }

    private void initPopup() {
        fxmlBean = RegionUtil.loadFxmlBean(this.getClass().getResource("/fxmls/CaptureControlPane.fxml"));
        fxmlBean.getController().setScreenCapturePane(this);
        controlPane.getChildren().add(fxmlBean.getNode());

        captureStage = new Stage();
        captureStage.setMaximized(true);
        Scene scene = new Scene(this);
        scene.setFill(Color.TRANSPARENT);
        captureStage.setScene(scene);
        captureStage.initOwner(StageUtil.getStage());
        captureStage.initStyle(StageStyle.TRANSPARENT);
        captureStage.initModality(Modality.APPLICATION_MODAL);
    }

    public void show(Consumer<String> captureConsumer) {
        tip.setVisible(true);
        positionLabel.setFont(Font.font(Font.getDefault().getFamily(), 22));
        positionLabel.setVisible(false);
        controlPane.setVisible(false);
        maskRectangle.setVisible(false);
//        canvas.setVisible(false);
        captureImage = null;
        canvasItems.clear();
        canCapture = true;
        textField.setText("");
        textField.setVisible(false);
        canvasItemChangePosition = false;

        ThreadPoolUtil.schedule(()->Platform.runLater(()->tip.setVisible(false)), 2000);
        rectangle.setWidth(0);
        rectangle.setHeight(0);

//        popup.show(webView.getScene().getWindow(), 0d, 0d);

//        StageUtil.getStage().hide();
        StageUtil.getStage().setIconified(true);
        captureStage.show();
//        ThreadPoolUtil.schedule(()->{
//            Platform.runLater(()->{
//                captureFullScreen();
//                captureStage.show();
//            });
//        }, 200);

        this.captureConsumer= captureConsumer;
    }

    public void callbackForCapture(String capture) {
        captureConsumer.accept(capture);
    }

    public void hide() {
        controlPane.setVisible(false);
//        popup.hide();
        captureStage.close();
//        StageUtil.getStage().show();
        StageUtil.getStage().setIconified(false);
//        StageUtil.getStage().getScene().getRoot().setVisible(false);
    }

    public String getCaptureImageUrl() {
//        Image image = canvas.snapshot(new SnapshotParameters(), null);
        File imageCacheFile = new File(imageCacheDir);
        if (!imageCacheFile.exists()) {
            imageCacheFile.mkdirs();
        }
        String file = imageCacheDir + File.separator + System.currentTimeMillis() + ".png";
        File imageFile = new File(file);
        saveImage(captureImage, imageFile);
        String captureImageUrl = imageFile.getAbsolutePath().replace("\\", "/");
        return captureImageUrl;
    }

    private void saveImage(Image image, File imageFile) {
        BufferedImage bImage = SwingFXUtils.fromFXImage(image, null);
//        System.out.println(String.format("image size: %s, %s, bi size: %s, %s", image.getWidth(), image.getHeight(), bImage.getWidth(), bImage.getHeight()));
        try {
            ImageIO.write(bImage, "png", imageFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void captureFullScreen() {
        double w = screen.getBounds().getWidth();
        double h = screen.getBounds().getHeight();
        WritableImage writableImage = captureScreen(0, 0, w, h, true);
//        String file = "F:\\tmp\\capture\\" + System.currentTimeMillis() + ".png";
//        File imageFile = new File(file);
//        saveImage(writableImage, imageFile);
        System.out.println(String.format("image size: %s， %s; screen size: %s, %s", writableImage.getWidth(), writableImage.getHeight(), w, h));
        fullScreenImageView.setImage(writableImage);
    }

    private void captureRectangleScreen() {
        if (rectangle.getHeight() < 1 || rectangle.getWidth() < 1) return;
        WritableImage writableImage = captureScreen(rectangle.getX()+1, rectangle.getY()+1, rectangle.getWidth()-2, rectangle.getHeight()-2, false); // -12
        captureImage = writableImage;
        graphicsContext.drawImage(writableImage, 1, 1, rectangle.getWidth()-2, rectangle.getHeight()-2);
    }

    private void initEvent() {
        this.setOnMousePressed(e->{
            if(!canCapture) return;
            positionLabel.setVisible(true);
            controlPane.setVisible(false);
            maskRectangle.setVisible(false);
//            origin = new Point2D(Math.round(e.getScreenX()), Math.round(e.getScreenY())); // +14
            origin = new Point2D(e.getScreenX(), e.getScreenY()); // +14
        });

        this.setOnMouseDragged(e->{
            if(!canCapture) return;

            double nx = e.getScreenX();
            double ny = e.getScreenY();
//            rectangle.setWidth(Math.round(Math.abs(nx-origin.getX())));
//            rectangle.setHeight(Math.round(Math.abs(ny-origin.getY())));

            rectangle.setWidth((int)Math.abs(nx-origin.getX()));
            rectangle.setHeight((int)Math.abs(ny-origin.getY()));

            if (nx >= origin.getX() && e.getScreenY() >= origin.getY()) {
                rectangle.setX(origin.getX());
                rectangle.setY(origin.getY());
            } else if (nx >= origin.getX() && ny < origin.getY()) {
                rectangle.setX(e.getScreenX());
                rectangle.setY(origin.getY());
            } else if (nx < origin.getX() && ny >= origin.getY()) {
                rectangle.setX(origin.getX());
                rectangle.setY(ny);
            } else {
                rectangle.setX(nx);
                rectangle.setY(ny);
            }
            rectangle.setX((int)rectangle.getX());
            rectangle.setY((int)rectangle.getY());
            calcPosition();
        });
        this.setOnMouseReleased(e->{
            if(!canCapture) return;
//            rectangle.setWidth(Math.round(Math.abs(Math.round(e.getScreenX())-origin.getX())));
//            rectangle.setHeight(Math.round(Math.abs(Math.round(e.getScreenY())-origin.getY())));

            rectangle.setWidth((int)Math.abs(e.getScreenX()-origin.getX()));
            rectangle.setHeight((int)Math.abs(e.getScreenY()-origin.getY()));
            calcControlPanePosition();
            captureRectangleScreen();
            maskRectangle.setVisible(true);
        });

        this.getScene().setOnKeyPressed(e->{
            if (e.getCode() == KeyCode.LEFT) {
                double nx = rectangle.getX() - 1;
                nx = nx > 0 ? nx : 0;
                rectangle.setX(nx);
                moveRectangle();
            } else if (e.getCode() == KeyCode.RIGHT) {
                double nx = rectangle.getX() + 1;
                nx = nx > 0 ? nx : 0;
                rectangle.setX(nx);
                moveRectangle();
            } else if (e.getCode() == KeyCode.UP) {
                double ny = rectangle.getY() - 1;
                ny = ny > 0 ? ny : 0;
                rectangle.setY(ny);
                moveRectangle();
            } else if (e.getCode() == KeyCode.DOWN) {
                double ny = rectangle.getY() + 1;
                ny = ny > 0 ? ny : 0;
                rectangle.setY(ny);
                moveRectangle();
            } else if (e.getCode() == KeyCode.ESCAPE) {
                this.hide();
            } else if(e.getCode() == KeyCode.ESCAPE) {
                captureStage.close();
            }
        });
        this.getScene().setOnKeyReleased(e->{
            if (canCapture) {
                calcPosition();
                controlPane.setVisible(true);
                rectangle.requestFocus();
                calcControlPanePosition();
            }
        });

        this.canvas.setOnMousePressed(e->{
            if(canCapture) return;
            currentCanvasPoint = new Point2D(e.getScreenX(), e.getScreenY());
            originCanvasPoint = new Point2D(currentCanvasItem.getX(), currentCanvasItem.getY());
            if(canvasItemChangePosition) return;
            if (currentCanvasItem.getType() == 2 || currentCanvasItem.getType() == 4) {
                List<CanvasPoint> canvasPoints = new ArrayList<>();
                currentCanvasItem.setCanvasPointList(canvasPoints);

                currentCanvasItem.setX(e.getScreenX());
                currentCanvasItem.setY(e.getScreenY());
            } else if (currentCanvasItem.getType() == 3) { // 输入文字
                textField.setVisible(true);
                textField.setText("");
                textField.setLayoutX(e.getScreenX());
                textField.setLayoutY(e.getScreenY()); // +14
                currentCanvasItem.setX(e.getScreenX());
                currentCanvasItem.setY(e.getScreenY());
                textField.requestFocus();
            }
            this.canvasItems.add(currentCanvasItem);
        });

        this.canvas.setOnMouseDragged(e->{
            if(canCapture) return;
            if (canvasItemChangePosition) {
                if (currentCanvasItem.getType() == 2 || currentCanvasItem.getType() == 4) {
                   return;
                }
                double cx = e.getScreenX() - currentCanvasPoint.getX();
                double cy = e.getScreenY() - currentCanvasPoint.getY();
                currentCanvasItem.setX(originCanvasPoint.getX() + cx);
                currentCanvasItem.setY(originCanvasPoint.getY() + cy);
                draw();
                return;
            }

            if (currentCanvasItem.getType() == 2 || currentCanvasItem.getType() == 4) {
                currentCanvasItem.getCanvasPointList().add(new CanvasPoint(e.getScreenX(), e.getScreenY()));
                draw();
                return;
            }

            currentCanvasItem.setWidth(Math.round(Math.abs(e.getScreenX()-currentCanvasPoint.getX())));
            currentCanvasItem.setHeight(Math.round(Math.abs(e.getScreenY()-currentCanvasPoint.getY())));
            if (e.getScreenX() >= currentCanvasPoint.getX() && e.getScreenY() >= currentCanvasPoint.getY()) {
                currentCanvasItem.setX(currentCanvasPoint.getX());
                currentCanvasItem.setY(currentCanvasPoint.getY());
            } else if (e.getScreenX() >= currentCanvasPoint.getX() && e.getScreenY() < currentCanvasPoint.getY()) {
                currentCanvasItem.setX(e.getScreenX());
                currentCanvasItem.setY(currentCanvasPoint.getY());
            } else if (e.getScreenX() < currentCanvasPoint.getX() && e.getScreenY() >= currentCanvasPoint.getY()) {
                currentCanvasItem.setX(currentCanvasPoint.getX());
                currentCanvasItem.setY(e.getScreenY());
            } else {
                currentCanvasItem.setX(e.getScreenX());
                currentCanvasItem.setY(e.getScreenY());
            }
            draw();
        });

        this.canvas.setOnMouseReleased(e->{
            if(canCapture) return;
            if (currentCanvasItem.getType() == 2 || currentCanvasItem.getType() == 4) {
                  setCanvasType(currentCanvasItem.getType());
                  return;
            }
            canvasItemChangePosition = true;
        });
    }

    private void initFullScreenCanvas() {
        fullScreenGc.clearRect(0, 0, getWidth(), getHeight());
        fullScreenGc.setFill(Color.rgb(0, 0, 0, 0.3));
        fullScreenGc.fillRect(0, 0, getWidth(), getHeight());
    }

    private void moveRectangle() {
        rectangle.requestFocus();
        controlPane.setVisible(false);
        calcPosition();
    }

    private void initBind() {
        canvas.widthProperty().bind(rectangle.widthProperty());
        canvas.heightProperty().bind(rectangle.heightProperty());
        rectangle.xProperty().addListener((InvalidationListener)l->canvas.setLayoutX(rectangle.getX()));
        rectangle.yProperty().addListener((InvalidationListener)l->canvas.setLayoutY(rectangle.getY()));

//        canvas.widthProperty().bind(rectangle.widthProperty().subtract(2));
//        canvas.heightProperty().bind(rectangle.heightProperty().subtract(2));
//        rectangle.xProperty().addListener((InvalidationListener)l->canvas.setLayoutX(rectangle.getX()+1));
//        rectangle.yProperty().addListener((InvalidationListener)l->canvas.setLayoutY(rectangle.getY()+1));
    }

    private void draw() {
        graphicsContext.setFill(Color.LIGHTGRAY);
        graphicsContext.fillRect(0, 0, rectangle.getWidth(), rectangle.getHeight());
        if(captureImage != null) {
            graphicsContext.drawImage(captureImage, 1, 1, captureImage.getWidth(), captureImage.getHeight());
        }
        canvasItems.forEach(canvasItem -> {
            if (canvasItem.getType() == 0) {
                graphicsContext.setStroke(canvasItem.getColor());
                graphicsContext.setLineWidth(2);
                graphicsContext.strokeRect(canvasItem.getX()-rectangle.getX(), canvasItem.getY()-rectangle.getY(), canvasItem.getWidth(), canvasItem.getHeight()); // +14
            } else if (canvasItem.getType() == 1) {
                graphicsContext.setStroke(canvasItem.getColor());
                graphicsContext.setLineWidth(2);
                graphicsContext.strokeOval(canvasItem.getX()-rectangle.getX(), canvasItem.getY()-rectangle.getY(), canvasItem.getWidth(), canvasItem.getHeight()); // +14
            } else if (canvasItem.getType() == 2 || canvasItem.getType() == 4) {  // 2 随便划线; 4 擦除数据
                if(canvasItem.getCanvasPointList().size() <1) return;
                graphicsContext.setFill(canvasItem.getColor());
                graphicsContext.setStroke(canvasItem.getColor());
                graphicsContext.setLineWidth(2);
                if(canvasItem.getType() == 4) {
                    graphicsContext.setLineCap(StrokeLineCap.ROUND);
                    graphicsContext.setLineWidth(10);
                }
                graphicsContext.beginPath();

                graphicsContext.moveTo(canvasItem.getX()-rectangle.getX(), canvasItem.getY()-rectangle.getY()); // +14
                for(CanvasPoint cp : canvasItem.getCanvasPointList()) {
                    graphicsContext.lineTo(cp.getX()-rectangle.getX(), cp.getY()-rectangle.getY()); // +14
                }
                graphicsContext.stroke();
            } else if (canvasItem.getType() == 3) {  // 文字
                graphicsContext.setFill(canvasItem.getColor());
                graphicsContext.setStroke(canvasItem.getColor());
                graphicsContext.fillText(canvasItem.getText(), canvasItem.getX()-rectangle.getX(), canvasItem.getY()-rectangle.getY()+18); // +14
            }
        });
    }

    private void calcPosition() {
        if (rectangle.getY() > 30) {
            positionLabel.setStyle("-fx-text-fill: white");
            positionLabel.setLayoutX(rectangle.getX());
            positionLabel.setLayoutY(rectangle.getY() - 30);
            positionLabel.setText(Math.round(rectangle.getWidth()-2) + ":" + Math.round(rectangle.getHeight()-2));
        } else {
            positionLabel.setStyle("-fx-text-fill: green");
            positionLabel.setLayoutX(rectangle.getX());
            positionLabel.setLayoutY(rectangle.getY() + 10);
            positionLabel.setText(Math.round(rectangle.getWidth()-2) + ":" + Math.round(rectangle.getHeight()-2));
        }
    }

    private void calcControlPanePosition() {
        controlPane.setVisible(true);
        if (rectangle.getY() < screen.getBounds().getHeight()-10) {
            controlPane.setLayoutX(rectangle.getX()+rectangle.getWidth()- 318);
            controlPane.setLayoutY(rectangle.getY()+rectangle.getHeight()+10);
        } else if(rectangle.getY() > 45){
            controlPane.setLayoutX(rectangle.getX() - 318);
            controlPane.setLayoutY(rectangle.getY() - 40);
        }
        rectangle.requestFocus();
    }

    private Screen getScreen(Node node) {
        List<Screen> screenList = Screen.getScreens();
//        System.out.println("screen size:" + screenList.size());
        for(Screen screen : screenList) {
            if (screen.getBounds().contains(node.localToScene(0, 0))) {
//                System.out.println("w:" + screen.getBounds().getWidth() + ", height:" + screen.getBounds().getHeight() +", maxX:" + screen.getBounds().getMaxX()
//                        + ", " + screen.getOutputScaleX() +  ", " + screen.getOutputScaleX());
                return screen;
            }
        }
        throw new RuntimeException("get Screen Error!!");
    }

    private WritableImage captureScreen(double x, double y, double w, double h, boolean scaleToFit) {
        return robot.getScreenCapture(null, x, y, w, h, scaleToFit);

    }

}
